package com.lagou.framework.servlet;

import com.lagou.framework.interceptor.AccountSecurityInterceptor;
import com.lagou.framework.interceptor.HandlerInterceptor;
import com.lagou.framework.ioc.AnnotationConfigApplicationContext;
import com.lagou.framework.mvc.MethodHandler;
import org.apache.commons.lang.StringUtils;
import org.junit.Assert;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * 控制器
 *
 * @author srh
 * @date 2020/03/14
 **/
public class DispatcherServlet extends HttpServlet {
    /**
     * 获取扫描包的配置
     */
    private static final String SCAN_PACKAGE = "scanPackage";
    /**
     * 所有的Handler
     */
    private Set<MethodHandler> handlerMapping;
    /**
     * 拦截器
     */
    private Set<HandlerInterceptor> interceptors;

    @Override
    public void init(ServletConfig config) throws ServletException {
        // 初始化拦截器
        interceptors = new HashSet<>() {{
            add(new AccountSecurityInterceptor());
        }};
        // 获取web.xml中init-params中初始化参数
        String scanPackage = getScanPackage(config.getInitParameter("mvcContextLocation"));
        if (StringUtils.isEmpty(scanPackage)) {
            System.err.println("DispatcherServlet.init failed, scanPackage: " + scanPackage);
            return;
        }
        // 获取到扫描包配置进行初始化IOC
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.scanPackage(scanPackage);
        // 得到所有的handlerMapping
        this.handlerMapping = context.getHandlers();
        System.out.println("DispatcherServlet.init success");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置浏览器字符集编码.
        resp.setHeader("Content-Type", "text/html;charset=UTF-8");
        // 设置response的缓冲区的编码.
        resp.setCharacterEncoding("UTF-8");

        MethodHandler methodHandler = getMethodHandlerByUri(req.getRequestURI());
        if (null == methodHandler) {
            resp.getWriter().write("404 未找到请求资源");
            return;
        }

        for (HandlerInterceptor interceptor : this.interceptors) {
            boolean allow = interceptor.preHandler(req, methodHandler);
            if (!allow && interceptor instanceof AccountSecurityInterceptor) {
                resp.getWriter().write("401 当前用户没有权限");
                return;
            }
        }

        Object[] args = transferMethodArgs(req, resp, methodHandler);
        try {
            methodHandler.getMethod().invoke(methodHandler.getInstance(), args);
        } catch (Throwable e) {
            System.err.println(String.format("%s executor failed, errorMsg: %s", req.getRequestURI(), e.getMessage()));
        }
    }

    private Object[] transferMethodArgs(HttpServletRequest req, HttpServletResponse resp, MethodHandler methodHandler) {
        Object[] args = new Object[methodHandler.getMethod().getParameterTypes().length];
        Map<String, String[]> reqParameterMap = req.getParameterMap();
        // req和res映射
        Integer reqIdx = methodHandler.getParameterMappings().get(HttpServletRequest.class.getSimpleName());
        if (null != reqIdx && reqIdx != -1) {
            args[reqIdx] = req;
        }
        Integer resIdx = methodHandler.getParameterMappings().get(HttpServletResponse.class.getSimpleName());
        if (null != resIdx && resIdx != -1) {
            args[resIdx] = resp;
        }
        // 普通参数映射
        for (Map.Entry<String, String[]> entry : reqParameterMap.entrySet()) {
            if (methodHandler.getParameterMappings().containsKey(entry.getKey())) {
                // name=1&name=2
                String val = StringUtils.join(entry.getValue(), ",");
                // method(req,res,name)
                Integer paramIndex = methodHandler.getParameterMappings().get(entry.getKey());
                if (null != paramIndex && paramIndex != -1) {
                    args[paramIndex] = val;
                }
            }
        }
        return args;
    }

    private MethodHandler getMethodHandlerByUri(String requestUri) {
        if (StringUtils.isNotBlank(requestUri)) {
            for (MethodHandler methodHandler : handlerMapping) {
                Pattern pattern = methodHandler.getPattern();
                if (pattern.matcher(requestUri).matches()) {
                    return methodHandler;
                }
            }
        }
        return null;
    }

    private String getScanPackage(String propertyContext) {
        Assert.assertNotNull(propertyContext);
        InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(propertyContext);
        Assert.assertNotNull(stream);
        Properties properties = new Properties();
        try {
            properties.load(stream);
            return properties.getProperty(SCAN_PACKAGE);
        } catch (IOException e) {
            System.err.println("DispatcherServlet.getScanPackage errorMsg: " + e.getMessage());
            return null;
        }
    }
}
